import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

import java.math.BigDecimal;

/**
 * Created by IntelliJ IDEA.
 * User: evgeniysorokin
 * Date: 18.09.12
 */
public class Main extends Application {

    public static final int N = 50;
    public static final int SIMULATIONS = 10;
    public static int CURRENT_SIMULATION = 0;
    public static final int SITE_SIZE = 10;
    public static final int BORDER_SIZE = 1;
    public static final Color BLOCKED_SITE_COLOR = Color.BLACK;
    public static final Color OPENED_SITE_COLOR = Color.WHITE;
    public static final Color FULL_OPENED_SITE_COLOR = Color.CYAN;
    public static int openSites = 0;
    public static Percolation p;
    
    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Group root = new Group();
        BorderPane borderPane = new BorderPane();
        StackPane stackPane = new StackPane();
        p = new Percolation(N);
        
        //Right panel - buttons
        VBox right = new VBox(5);
        right.setPadding(new Insets(5));
        Button resetButton = new Button("reset");
        Button startButton = new Button("start");
        Button stopButton = new Button("stop");

        right.getChildren().addAll(resetButton, startButton, stopButton);

        borderPane.setRight(right);
        
        //Left panel - threshold results
        final VBox left = new VBox(2);
        left.setPadding(new Insets(5));
        left.getChildren().add(new Label("results:"));

        borderPane.setLeft(left);

        //Bottom, percolate status
        final Label percolatesLabel = new Label("system does not percolate");
        percolatesLabel.setTextFill(Color.RED);
        stackPane.getChildren().add(percolatesLabel);
        borderPane.setBottom(stackPane);

        //Main scene - center
        Scene scene = new Scene(borderPane, (N + 2) * SITE_SIZE + N * BORDER_SIZE + 100, (N + 2) * SITE_SIZE + N * BORDER_SIZE, Color.LIGHTGRAY);
        borderPane.setCenter(root);
        primaryStage.setScene(scene);
        primaryStage.show();

        //Information - top
        HBox top = new HBox(5);
        top.setPadding(new Insets(5));
        final Label openSitesLabel = new Label("open sites: 0");
        top.getChildren().addAll(new Label("sites: " + N * N ), new Label("/"), openSitesLabel);
                
        borderPane.setTop(top);

        resetButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                p = new Percolation(N);
                openSites = 0;
                openSitesLabel.setText("open sites: 0");
                percolatesLabel.setText("system does not percolate");
                percolatesLabel.setTextFill(Color.RED);
            }
        });

        final Thread simulThread = new Thread() {
            @Override
            public void run() {
                for(int s = 0; s < SIMULATIONS; s++) {

                    while(!p.percolates()) {
                        try {
                            int i = StdRandom.uniform(1, N + 1), j = StdRandom.uniform(1, N + 1);
                            if(!p.isOpen(i, j)) {
                                p.open(i, j);

                                Platform.runLater(new Runnable() {
                                    @Override
                                    public void run() {
                                        openSites++;
                                        openSitesLabel.setText("open sites: " + openSites);
                                    }
                                });
                                Thread.sleep(1000/10);
                            }
                        } catch (IndexOutOfBoundsException e) {
                            System.out.println("outOfBound");
                        } catch (InterruptedException ie) {
                            System.out.println("interrupted");
                        }
                    }

                    p = new Percolation(N);

                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            CURRENT_SIMULATION++;
                            BigDecimal threshold;
                            threshold = new BigDecimal(openSites).divide(new BigDecimal(N * N));
                            percolatesLabel.setText("system does percolate, threshold = " + threshold);
                            percolatesLabel.setTextFill(Color.GREEN);
                            left.getChildren().add(new Label("p" + CURRENT_SIMULATION + "=" + threshold));

                            openSites = 0;
                            openSitesLabel.setText("open sites: 0");
                            percolatesLabel.setText("system does not percolate");
                            percolatesLabel.setTextFill(Color.RED);
                        }
                    });
                }
            }
        };

        stopButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                simulThread.suspend();
                p = new Percolation(N);
                openSites = 0;
                openSitesLabel.setText("open sites: 0");
                percolatesLabel.setText("system does not percolate");
                percolatesLabel.setTextFill(Color.RED);
            }
        });

        startButton.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent actionEvent) {
                if(simulThread.isAlive())
                    simulThread.resume();
                else
                    simulThread.start();
            }
        });

        final Site[][] sites = new Site[N + 1][N + 1];
                
        final Thread thread = new Thread() {
            @Override
            public void run() {
                while(true) {
                    for(int i = 1; i <= N; i++)
                        for(int j = 1; j <= N; j++) {
                            Site site = sites[i][j];
                            
                            int siteI = site.getI(), siteJ = site.getJ();
                            if(!p.isOpen(siteI, siteJ))
                                site.setFill(BLOCKED_SITE_COLOR);
                            else
                                site.setFill(OPENED_SITE_COLOR);
                            if(p.isFull(siteI, siteJ)) site.setFill(FULL_OPENED_SITE_COLOR);
                        }       
                    
                    try {
                        Thread.sleep(1000/10);
                    } catch (InterruptedException e) {
                        System.out.println("Interrupted");
                    }
                }
            }
        };

        primaryStage.addEventHandler(WindowEvent.WINDOW_CLOSE_REQUEST, new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent windowEvent) {
                thread.stop();
                simulThread.stop();
            }
        });
                
        Group sitesGroup = new Group();
        for(int i = 1; i <= N; i++)
            for(int j = 1; j <= N; j++) {
                Site site = new Site(SITE_SIZE, SITE_SIZE, BLOCKED_SITE_COLOR);
                site.setY(i * (SITE_SIZE + BORDER_SIZE));
                site.setX(j * (SITE_SIZE + BORDER_SIZE));
                site.setI(i);
                site.setJ(j);
                sites[i][j] = site;
                
                site.addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
                    @Override
                    public void handle(MouseEvent mouseEvent) {
                        Site s = (Site) mouseEvent.getSource();
                        
                        int i = s.getI(), j = s.getJ();
                        
                        System.out.println(s.getI() + ":" + s.getJ());

                        try {
                            p.open(i, j);
                            openSites++;
                            openSitesLabel.setText("open sites: " + openSites);
                            
                            if(p.percolates()) {
                                BigDecimal threshold;
                                threshold = new BigDecimal(openSites).divide(new BigDecimal(N * N));
                                percolatesLabel.setText("system does percolate, threshold = " + threshold);
                                percolatesLabel.setTextFill(Color.GREEN);
                            }
                        } catch (IndexOutOfBoundsException e) {
                            System.out.println("outOfBound");
                        }
                        
                    }
                });
                
                sitesGroup.getChildren().add(site);
            }        

        thread.start();

        root.getChildren().add(sitesGroup);
    }
    
    public static class Site extends Rectangle {
        private int i, j;
        
        public Site(int width, int height, Color color) {
            super(width, height, color);
        }

        public int getI() {
            return i;
        }

        public void setI(int i) {
            this.i = i;
        }

        public int getJ() {
            return j;
        }

        public void setJ(int j) {
            this.j = j;
        }
    }
}
